/** 获取页面最大的z-index + 1
 * 
 * 注意：z-index 和层级关系有以下几个特点
 * 
 * - z-index 不能用于静态布局的元素
 * - 非静态布局的元素层级高于静态元素
 * - z-index 只对同一个父元素(stricky 和 fixed)下的元素进行层叠比较，不同父元素下的 z-index 是互相独立的
 * - z-index 默认为 auto，则会按照 非静态布局 > 静态布局 的顺序进行层叠，z-index: 0 小于 z-index: auto
 * 
 * @returns {number}
 * 
 * @example
 * <!-- 父容器静态布局，z-index 有效 -->
 * <div><div style="position:absolute; z-index:1" /></div>
 * <div><div style="position:relative" /></div>
 * 
 * <!-- 父容器 fixed 布局，z-index 仅在父容器内有效，会被后面的 relative 层挡住 -->
 * <div style="position:fixed"><div style="position:absolute; z-index:1" /></div>
 * <div><div style="position:relative" /></div>
 */
export function maxZ() {
  let max = 0;
  traverseDOM(document.body, i => {
    const z = +getComputedStyle(i).zIndex;
    if (isNaN(z))
      return;
    if (z > max)
      max = z;
    // 父元素有了zIndex子元素的zIndex就不生效了，停止遍历节省效率
    return true;
  })
  return max + 1;
}

/** 从父元素开始遍历 DOM 元素（递归）
 * @param {HTMLElement} e - 要遍历的当前元素
 * @param {function(HTMLElement):boolean} func - 返回是否要停止当前元素的遍历
 */
export function traverseDOM(e, func) {
  if (func(e))
    return true;
  for (const child of e.children)
    // 递归处理子元素
    if (traverseDOM(child, func))
      return true;
}
/** 从父元素开始遍历 DOM 元素的节点（递归）
 * @param {HTMLElement} e - 要遍历的当前元素
 * @param {function(Node):boolean} func - 返回是否要停止当前元素的遍历
 */
export function traverseNode(e, func) {
  if (func(e))
    return true;
  for (const child of e.childNodes)
    // 递归处理子元素
    if (traverseNode(child, func))
      return true;
}
/** 从子元素开始遍历 DOM 元素（递归）
 * @param {HTMLElement} e - 要遍历的当前元素
 * @param {function(HTMLElement):boolean} func - 返回是否要停止当前元素的遍历
 */
export function traverseDOM2(e, func) {
  for (const child of e.children)
    // 递归处理子元素
    if (traverseDOM(child, func))
      return true;
  if (func(e))
    return true;
}
/** 从指定节点开始遍历 DOM 元素，然后往节点的兄弟节点继续遍历整个文档
 * @param {HTMLElement} e - 要开始遍历的元素
 * @param {function(HTMLElement):boolean} func - 返回是否要停止当前元素的遍历
 * @param {number} offset - 要开始遍历的元素的兄弟节点索引偏移值，例如从节点下一个兄弟节点开始则为1
 */
export function traverseFrom(e, func, offset) {
  while (e != document.body) {
    let i = 0;
    for (; i < e.parentElement.children.length; i++)
      if (e.parentElement.children[i] == e)
        break;
    i += offset;
    for (; i < e.parentElement.children.length; i++)
      if (traverseDOM(e.parentElement.children[i], func))
        return;
    e = e.parentElement;
  }
}

/** 相当于按 tab 键的操作，让光标聚焦下一个元素
 * 
 * 注意：ee-ui 框架并不会设置 tabindex 为 -1 或 0 以外的值
 * 
 * 所以此方法不会对 tabindex 进行排序，仅仅只是按照元素在 DOM 元素中的顺序聚焦下一个元素
 * @param {boolean} [loop] - 到底之后是否循环到第一个元素
 * @returns {HTMLElement} - tab 后聚焦的 DOM 元素
 */
export function tab(loop) {
  let focus = document.querySelector(':focus');
  const func = e => {
    // 从头开始tab时又是只有自己可以聚焦，则也不聚焦
    if (e == focus) {
      focus = undefined;
      return true;
    }
    if (isFocusable(e)) {
      focus = e;
      e.focus();
      return true;
    }
  }
  if (focus) {
    const temp = focus;
    traverseFrom(focus, func, 1);
    // 后面没有可聚焦的元素
    if (temp == focus) {
      // 从头开始聚焦
      if (loop)
        traverseDOM(document.body, func);
      else
        focus = undefined;
    }
  } else {
    traverseDOM(document.body, func);
  }
  return focus;
}

/** 一个 DOM 元素是否可以获得焦点
 * @param {HTMLElement} e - DOM 元素
 * @returns {boolean} - 是否可以获得焦点
 */
export function isFocusable(e) {
  // hidden 相当于 [hidden] { display: none }
  if (e.hidden)
    return false;
  if (e.contentEditable != 'true') {
    // <div contenteditable> 可聚焦 (tabindex 为 1)
    if (e.tabIndex == -1)
      return false;
    if (e.nodeName == 'A' && !e.href)
      return false;
  } else {
    // <div contenteditable tabindex="-1"> 不可聚焦
    if (e.getAttribute('tabindex') == -1)
      return false;
  }
  // disabled 普通 div 设置了 tabIndex 和 disabled 任然可以获得焦点
  if (e.nodeName == 'INPUT' && e.getAttribute('type')?.toLowerCase() == 'hidden')
    return false;
  if ((e.nodeName == 'INPUT' || e.nodeName == 'TEXTAREA' || e.nodeName == 'BUTTON' || e.nodeName == 'SELECT')
    && e.getAttribute('disabled') != null)
    return false;
  const style = getComputedStyle(e);
  if (style.display == 'none' || style.visibility == 'hidden' || style.visibility == 'collapse')
    return false;
  return e.tabIndex > -1
    || e.contentEditable == 'true'
    || (e.nodeName == 'A' && e.href)
    || (e.nodeName == 'INPUT' || e.nodeName == 'TEXTAREA' || e.nodeName == 'BUTTON' || e.nodeName == 'SELECT');;
}

/** 判断一个 DOM 元素里面的子元素宽度是否超过 DOM 元素的宽度(是否换行)
 * @param {HTMLElement} el - 父元素，一般父元素采用 flex 横向布局，并且 overflow-x 为 hidden，可以使用类样式 .ee-layout
 */
export function isOverflowX(el) {
  const s1 = getComputedStyle(el);
  /** 内容宽度 = 父容器 padding 左右 + (首个子元素 margin-left - left) + (末尾子元素 margin-right + right) */
  let content = parseInt(s1.paddingLeft) + parseInt(s1.paddingRight);
  if (el.children.length == 0)
    return false;
  const s2 = getComputedStyle(el.children[0]);
  content += parseInt(s2.marginLeft) - el.children[0].getBoundingClientRect().x;
  if (el.children.length > 1) {
    const last = el.children[el.children.length - 1];
    const s3 = getComputedStyle(last);
    content += parseInt(s3.marginRight) + last.getBoundingClientRect().right;
  } else {
    content += parseInt(s2.marginRight);
  }
  console.log("overflow-x", (content | 0), el.offsetWidth)
  return (content | 0) > el.offsetWidth;
}

/** 调用preventDefault不执行浏览器默认行为
 * @param {Event} e
 */
export function prevent(e) {
  e.preventDefault();
}

/** 调用stopPropagation阻止行为冒泡
 * @param {Event} e
 */
export function stop(e) {
  e.stopPropagation();
}

/** 将一个带单位的样式数值转换成像素为单位的数值
 * @param {HTMLElement} e - 样式所述的 DOM 元素
 * @param {string} value - CSS 样式数值，例如 5px, 1em, 1rem, 1vw, 1% 等
 * @returns {number}
 */
export function toPx(e, value) {

}

/** 将 class 对象转换成 '-' 分割的字符串，方便 [class*=-***] 的样式生效
 * @param {Record<string, string | boolean | number>} obj - class 对象，例如 { color: 'primary', plain: 1, button: false }
 * @param {string} prefix - class 的前缀名
 * @returns 字符串则为字符串，数字或boolean值则为key，obj 参数的例子，则为 -primary-plain
 * @example
 * const color = 'primary';
 * const plain = true;
 * const label = false;
 * classString({ color, plain, label }, 'eec')
 */
export function classString(obj, prefix) {
  const array = [];
  if (prefix)
    array.push(prefix);
  for (const key in obj) {
    const value = obj[key];
    if (value) {
      if (value.constructor == String)
        array.push(value);
      else
        array.push(key);
    }
  }
  return array.join('-');
}